package com.zz.framework.core.ext

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.lang.reflect.InvocationHandler
import java.lang.reflect.Method
import java.lang.reflect.Proxy
import java.util.Locale
import kotlin.coroutines.CoroutineContext

/**
 * Copyright © 2020 zanezhao All rights reserved.
 * Created on 2020/7/2 2:00 PM.
 * @version: V1.0
 *
 *
val onNumberChange: (Int) -> Unit = Throttle.throttleLatest(
450L,
Dispatchers.Main
) {
it.printError()
}
 */
fun <T> Any.throttleLatest(
    intervalMs: Long = 300L,
    coroutineScope: CoroutineScope,
    destinationFunction: (T) -> Unit
): (T) -> Unit {
    var throttleJob: Job? = null
    var latestParam: T
    return { param: T ->
        latestParam = param
        if (throttleJob?.isCompleted != false) {
            throttleJob = coroutineScope.launch {
                delay(intervalMs)
                latestParam.let(destinationFunction)
            }
        }
    }
}

fun <T> Any.throttleLatest(
    intervalMs: Long = 300L,
    coroutineContext: CoroutineContext,
    destinationFunction: (T) -> Unit
): (T) -> Unit {
    var throttleJob: Job? = null
    var latestParam: T
    return { param: T ->
        latestParam = param
        if (throttleJob?.isCompleted != false) {
            throttleJob = CoroutineScope(coroutineContext).launch {
                delay(intervalMs)
                latestParam.let(destinationFunction)
            }
        }
    }
}

/**
 * @receiver Any
 * @param skipMs Long
 * @param coroutineScope CoroutineScope
 * @param destinationFunction Function1<T, Unit>
 * @return (T) -> Unit
 */
fun <T> Any.throttleFirst(
    skipMs: Long = 300L,
    coroutineScope: CoroutineScope,
    destinationFunction: (T) -> Unit
): (T) -> Unit {
    var throttleJob: Job? = null
    return { param: T ->
        if (throttleJob?.isCompleted != false) {
            throttleJob = coroutineScope.launch {
                destinationFunction(param)
                delay(skipMs)
            }
        }
    }
}


/**
 *
 * @receiver T
 * @param during Long 间隔时长 默认500L
 * @return T
 */
fun <T> T.throttle(during: Long = 500L): T {
    return Proxy.newProxyInstance(this!!::class.java.classLoader, this!!::class.java.interfaces,
        object : InvocationHandler {

            private var lastInvokeTime = System.currentTimeMillis()

            override fun invoke(proxy: Any, method: Method, args: Array<out Any>?): Any? {
                val current = System.currentTimeMillis()
                return if (current - lastInvokeTime > during) {
                    lastInvokeTime = current;
                    method.invoke(this@throttle, *args.orEmpty())
                } else {
                    resolveDefaultReturnValue(method)
                }
            }
        }
    ) as T
}

private fun resolveDefaultReturnValue(method: Method): Any? {
    return when (method.returnType.name.lowercase(Locale.US)) {
        Void::class.java.simpleName.lowercase(Locale.US) -> null
        else -> throw IllegalArgumentException("throttle只能作用于无返回值函数")
    }
}



fun <T> Any.debounce(
    waitMs: Long = 300L,
    coroutineScope: CoroutineScope,
    destinationFunction: (T) -> Unit
): (T) -> Unit {
    var debounceJob: Job? = null
    return { param: T ->
        debounceJob?.cancel()
        debounceJob = coroutineScope.launch {
            delay(waitMs)
            destinationFunction(param)
        }
    }
}

/**
 * @param delayMs Long
 * @param coroutineContext CoroutineContext
 * @param destinationFunction Function1<T, Unit>
 * @return (T) -> Unit
 */
fun <T> Any.debounce(
    delayMs: Long = 500L,
    coroutineContext: CoroutineContext,
    destinationFunction: (T) -> Unit
): (T) -> Unit {
    var debounceJob: Job? = null
    return { param: T ->
        if (debounceJob?.isCompleted != false) {
            debounceJob = CoroutineScope(coroutineContext).launch {
                delay(delayMs)
                destinationFunction(param)
            }
        }
    }
}